#include "lcd_cmf1p2301.hpp"
#include "commtick.hpp"
#define LCD_CMF1P2301_DELAY		1
#define LCD_CMF1P2301_DELAY2	100

using namespace device;

uint32_t lcd_cmf1p2301::uiTick  = 0;

const lcd::LcdAttributeTPDF LcdAttribute = 
{
	lcd::COLOR_TYPE_MONOCHROME,{122,32},100
};

const lcd::LcdAttributeTPDF* lcd_cmf1p2301::GetLcdAttribute(void)
{
	return(&LcdAttribute);
}

void lcd_cmf1p2301::vLcdCommand(uint8_t cmd)
{
	volatile uint32_t i;
	uint8_t pin = mLcdCmf1p2301Init.DataLsbPin.GpioPin;
	
	gpio::Write(mLcdCmf1p2301Init.Cd.GpioPort, mLcdCmf1p2301Init.Cd.GpioPin, 0);
	for(i = 0; i < LCD_CMF1P2301_DELAY; i++){}
	gpio::Write(mLcdCmf1p2301Init.Cs.GpioPort, mLcdCmf1p2301Init.Cs.GpioPin, 0);
	for(i = 0; i < LCD_CMF1P2301_DELAY; i++){}
	gpio::Modify(mLcdCmf1p2301Init.DataLsbPin.GpioPort, ~(0xff << pin), cmd << pin);
	for(i = 0; i < LCD_CMF1P2301_DELAY; i++){}
	gpio::Write(mLcdCmf1p2301Init.Cs.GpioPort, mLcdCmf1p2301Init.Cs.GpioPin, 1);
}

void lcd_cmf1p2301::vLcdData(uint8_t data)
{
	volatile uint32_t i;
	uint8_t pin = mLcdCmf1p2301Init.DataLsbPin.GpioPin;
	
	gpio::Write(mLcdCmf1p2301Init.Cd.GpioPort, mLcdCmf1p2301Init.Cd.GpioPin, 1);
	for(i = 0; i < LCD_CMF1P2301_DELAY; i++){}
	gpio::Write(mLcdCmf1p2301Init.Cs.GpioPort, mLcdCmf1p2301Init.Cs.GpioPin, 0);
	for(i = 0; i < LCD_CMF1P2301_DELAY; i++){}
	gpio::Modify(mLcdCmf1p2301Init.DataLsbPin.GpioPort, ~(0xff << pin), data << pin);	
	for(i = 0; i < LCD_CMF1P2301_DELAY; i++){}
	gpio::Write(mLcdCmf1p2301Init.Cs.GpioPort, mLcdCmf1p2301Init.Cs.GpioPin, 1);
}

bool lcd_cmf1p2301::LcdTimerTick(void)
{
   lcd_cmf1p2301::uiTick++;
   return (true);
}
	
lcd_cmf1p2301::lcd_cmf1p2301(LcdCmf1p2301InitTPDF *tLcdCmf1p2301Init)
{
	memcpy(&mLcdCmf1p2301Init, tLcdCmf1p2301Init, sizeof(LcdCmf1p2301InitTPDF));
    commtick::RegisteTick(LcdTimerTick,1);
	lcd_cmf1p2301::uiTick =0;
}

uint32_t lcd_cmf1p2301::uiGetTick(void)
{
	return lcd_cmf1p2301::uiTick;
}	

void  lcd_cmf1p2301::vSetTick(uint32_t tick)
{
	lcd_cmf1p2301::uiTick = tick;
}	

void lcd_cmf1p2301::Reset(void)
{
	volatile uint32_t i;
	
	gpio::Write(mLcdCmf1p2301Init.Rst.GpioPort, mLcdCmf1p2301Init.Rst.GpioPin, 0);
	gpio::Write(mLcdCmf1p2301Init.Cd.GpioPort, mLcdCmf1p2301Init.Cd.GpioPin, 1);
	gpio::Write(mLcdCmf1p2301Init.Cs.GpioPort, mLcdCmf1p2301Init.Cs.GpioPin, 1);
	gpio::Write(mLcdCmf1p2301Init.Wr0.GpioPort, mLcdCmf1p2301Init.Wr0.GpioPin, 0);
	gpio::Write(mLcdCmf1p2301Init.Wr1.GpioPort, mLcdCmf1p2301Init.Wr1.GpioPin, 1);
	for(i = 0; i < LCD_CMF1P2301_DELAY2; i++){}
	gpio::Write(mLcdCmf1p2301Init.Rst.GpioPort, mLcdCmf1p2301Init.Rst.GpioPin, 1);
	
	vLcdCommand(0xE2); //Set system reset
	for(i = 0; i < LCD_CMF1P2301_DELAY2; i++){} 

	vLcdCommand(0x26);//Set temperature compensationct  
	vLcdCommand(0xA0);//Set frame rate      
	vLcdCommand(0xC4);//set LCD mapping control
	vLcdCommand(0x2E); //Set Power control
	for(i = 0; i < LCD_CMF1P2301_DELAY2; i++){}  

	vLcdCommand(0xE9);//Set LCD Bias Ratio 
	for(i = 0; i < LCD_CMF1P2301_DELAY2; i++){}

	vLcdCommand(0x81);//
  vLcdCommand(0xd0);//Set contrast 
  for(i = 0; i < LCD_CMF1P2301_DELAY2; i++){}

  vLcdCommand(0x85);//set partial display control
  vLcdCommand(0xA6);//Reverse display ON / OFF      
  vLcdCommand(0xb0);//Set page address                
  vLcdCommand(0x00);//Set column address LSB                       
  vLcdCommand(0x10);//Set column address MSB    
  vLcdCommand(0x89);// 
  vLcdCommand(0xF2);//Set DST
  vLcdCommand(0x20);//
  vLcdCommand(0xF3);//Set DEN
  vLcdCommand(0x3f);//
  for(i = 0; i < LCD_CMF1P2301_DELAY2; i++){}
	
  vLcdCommand(0x40);// Set Scroll                
  vLcdCommand(0xAF);//Set display enable  
}

void lcd_cmf1p2301::Clear(void)
{
	uint8_t col, i;
	for(col = 0xb0; col < 0xb4; col++)
	{
		vLcdCommand(col);
		vLcdCommand(0x00);
		vLcdCommand(0x10);
		for(i = 0 ; i < 128; i++)
		{
			vLcdData(0);
		}
	}
	memset(mBuff,0,sizeof(mBuff));
}

void lcd_cmf1p2301::DrawHorizontal(uint32_t uiStartX, uint32_t uiStartY, const uint8_t *ucDots, uint32_t Len)
{
	uint8_t i, j, k, l;
	
	for(i = 0 ; (i < Len) && ((i + uiStartX) < LcdAttribute.Resolution.X); i++)
	{
		j = mBuff[uiStartX + i][uiStartY >> 3];
		k = j & (1 << (uiStartY & 7));
		l = 0;
		if((k == 0) && (ucDots[i >> 3] & (1 << (i & 7))))
		{
			mBuff[uiStartX + i][uiStartY >> 3] |= 1 << (uiStartY & 7);
			l = 1;
		}
		else if(k && ((ucDots[i >> 3] & (1 << (i & 7))) == 0))
		{
			mBuff[uiStartX + i][uiStartY >> 3] &= ~(1 << (uiStartY & 7));
			l = 1;
		}
		if(l)
		{
			vLcdCommand(0xb0 + (uiStartY >> 3));
			vLcdCommand((uiStartX + i) & 0x0f);
			vLcdCommand((((uiStartX + i) >> 4) & 0x0f)| 0x10);
			vLcdData(mBuff[uiStartX + i][uiStartY >> 3]);
		}
	}
}

void lcd_cmf1p2301::DrawHorizontal(uint32_t uiStartX, uint32_t uiStartY, const uint8_t *ucDots, lcd::RGBTPDF *tDots, uint32_t Len)
{
	
}

void lcd_cmf1p2301::DrawVertical(uint32_t uiStartX, uint32_t uiStartY, const uint8_t *ucDots, uint32_t Len)
{
	const uint8_t at[] = {0xff,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff};
	uint32_t i, j, k, l, m;
	uint8_t *c;
	uint8_t ucDotsCp[4];

	if(uiStartY + Len > LcdAttribute.Resolution.Y || (Len == 0))
	{
		return;
	}
	i = mBuff[uiStartX][0] + (mBuff[uiStartX][1] << 8) + (mBuff[uiStartX][2] << 16) + (mBuff[uiStartX][3] << 24);
	k = Len >> 3;
	if(Len & 7)
	{
		k++;
	}
	memcpy(ucDotsCp, ucDots, k);
	ucDotsCp[k - 1] &= at[Len & 7];
	l = 0;
	for(j = 0; j < k; j++)
	{
		l += ucDotsCp[j] << (j * 8);
	}
	l <<= uiStartY;
	m = 0;
	for(j = 0; j < uiStartY; j++)
	{
		m += 1 << j;
	}
	for(j = uiStartY + Len; j < 32; j++)
	{
		m += 1 << j;
	}
	l = (i & m) | l;
	c = (uint8_t *)&l;
	for(j = 0; j < 4; j++)
	{
		if(c[j] != mBuff[uiStartX][j])
		{
			mBuff[uiStartX][j] = c[j];
			vLcdCommand(0xb0 + j);
			vLcdCommand(uiStartX & 0x0f);
			vLcdCommand(((uiStartX >> 4) & 0x0f)| 0x10);
			vLcdData(c[j]);
		}
	}
}

void lcd_cmf1p2301::DrawVertical(uint32_t uiStartX, uint32_t uiStartY, const uint8_t *ucDots, lcd::RGBTPDF *tDots, uint32_t Len)
{
	
}
